using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using System.Reflection;
using Microsoft.SharePoint;
using System.Diagnostics;
using CKS.Utilities;

namespace CKS.EWE.WikiDiscussion
{
    public class CoordinateActions : SPItemEventReceiver
    {
        static CoordinateActions()
        {
            //Assembly thisAssembly = Assembly.GetExecutingAssembly();
            //exeName = thisAssembly.FullName;
            exeName = "WikiDiscussionEvents";
            productName = "CKS:EWE";
        }

        static string exeName, productName;

        /// <summary>
        /// ItemUpdated event is raised after a Wiki page is saved.
        /// Save could be from UI "Ok" after create or edit, or from code "Update()" after internal change.
        /// Renaming changes are handled here to coordinate metadata changes only; 
        /// Renaming changes are primarily handled in ItemFileMoved to rename the discussion item.
        /// </summary>
        /// <param name="properties"></param>
        public override void ItemUpdated(SPItemEventProperties properties)
        {
            string WikiTopicName = "";
            SPListItem DiscussionItem = null;

            Utilities.TraceProvider.RegisterTraceProvider();

            try
            {
                WikiTopicName = properties.ListItem.Name;
                LogMessage("Wiki topic '" + WikiTopicName + "' was updated; updating associated discussion topic.");
                if (rexFilename.Match(WikiTopicName).Success)
                {
                    WikiTopicName = rexFilename.Match(WikiTopicName).Result("$1");
                }

                DiscussionItem = GetDiscussionItem(properties);
                if (DiscussionItem != null)
                {
                    if (DiscussionItem["Title"].ToString() != WikiTopicName)
                    {
                        DiscussionItem["Title"] = WikiTopicName;
                        DiscussionItem.Update();
                    }
                }
            }
            catch (Exception ex)
            {
                LogMessage("Failed to update discussion entry for wiki topic '" + WikiTopicName + "'.  Common cause is that the ID for the discussion item was not set properly in Wiki Topic item.  In this case you should update manually, or delete discussion item and re-create from Wiki's Discuss function.", ex);
            }

            Utilities.TraceProvider.UnregisterTraceProvider();

            base.ItemUpdated(properties);
        }


        /// <summary>
        /// ItemUpdated event is raised after a Wiki page is moved, which happens when its name is changed.
        /// </summary>
        /// <param name="properties"></param>
        public override void ItemFileMoved(SPItemEventProperties properties)
        {
            string WikiTopicName = "";
            string WikiDiscussionUrl = "";
            SPListItem DiscussionItem = null;

            Utilities.TraceProvider.RegisterTraceProvider();

            try
            {
                WikiTopicName = properties.ListItem.Name;
                LogMessage("Wiki topic '" + WikiTopicName + "' was moved, probably because it was renamed; applying same change to discussion item.");
                if (rexFilename.Match(WikiTopicName).Success)
                {
                    WikiTopicName = rexFilename.Match(WikiTopicName).Result("$1");
                }

                DiscussionItem = GetDiscussionItem(properties);
                if (DiscussionItem != null)
                {
                    // Move to folder URL matching Wiki Topic's file URL.  
                    // A folder URL doesn't have .aspx, just the folder name.
                    if (rexUrlLeafname.Match(DiscussionItem.Folder.Url).Success)
                    {
                        // Build new discussion URL from current path plus new folder name
                        WikiDiscussionUrl = rexUrlLeafname.Match(DiscussionItem.Folder.Url).Result("$1");
                        WikiDiscussionUrl += "/" + WikiTopicName;
                    }
                    else
                    {
                        // No current path (folder's at root web); Build new discussion URL from new folder name only
                        WikiDiscussionUrl = WikiTopicName;
                    }

                    // Move discussion folder to new URL if it's different (should be, for Moved events)
                    if (WikiDiscussionUrl != DiscussionItem.Folder.Url)
                    {
                        DiscussionItem.Folder.MoveTo(WikiDiscussionUrl);
                    }
                }
            }
            catch (Exception ex)
            {
                LogMessage("Failed to move/rename discussion entry for wiki topic '" + WikiTopicName + "'.  Common cause is that the ID for the discussion item was not set properly in Wiki Topic item.  In this case you should update manually, or delete discussion item and re-create from Wiki's Discuss function.", ex);
            }

            Utilities.TraceProvider.UnregisterTraceProvider();

            base.ItemFileMoved(properties);
        }


        /// <summary>
        /// ItemDeleting event is raised just before a Wiki page is deleted.
        /// </summary>
        /// <param name="properties"></param>
        public override void ItemDeleting(SPItemEventProperties properties)
        {
            string WikiTopicName = "";
            SPListItem DiscussionItem = null;
            Guid DiscussionGuid = Guid.Empty;

                Utilities.TraceProvider.RegisterTraceProvider();

            try
            {
                WikiTopicName = properties.ListItem.Name;
                LogMessage("Wiki topic '" + WikiTopicName + "' being deleted; deleting associated discussion topic.");

                DiscussionItem = GetDiscussionItem(properties);
                if (DiscussionItem != null)
                {
                    DiscussionGuid = DiscussionItem.Recycle();
                }
            }
            catch (Exception ex)
            {
                LogMessage("Failed to delete discussion entry for wiki topic '" + WikiTopicName + "'.  Common cause is that the ID for the discussion item was not set properly in Wiki Topic item. In this case you should delete manually.", ex);
            }

            Utilities.TraceProvider.UnregisterTraceProvider();

            base.ItemDeleting(properties);
        }


        /// <summary>
        /// Retrieve a discussion item based on specification of the Wiki page in properties argument
        /// </summary>
        /// <param name="properties"></param>
        /// <returns>DiscussionItem</returns>
        private SPListItem GetDiscussionItem(SPItemEventProperties properties)
        {
            SPWeb WikiWeb = null;
            SPListItemCollection DiscussionItems = null;
            SPListItem DiscussionItem = null;
            string DiscussionListGuidString = "";
            string DiscussionGuidString = "";
            Guid DiscussionListGuid = Guid.Empty;
            Guid DiscussionGuid = Guid.Empty;

            // Don't register/unregister trace provider because 
            // this is called from routines that already do that.

            try
            {

                WikiWeb = new SPSite(properties.SiteId).OpenWeb(properties.RelativeWebUrl);

                // Get the Items from the List
                DiscussionListGuidString = properties.ListItem.Properties["DiscListGuid"].ToString();
                DiscussionListGuid = (Guid)TypeDescriptor.GetConverter(typeof(Guid)).ConvertFrom(DiscussionListGuidString);
                DiscussionItems = WikiWeb.Lists[DiscussionListGuid].Folders;

                // Get the Item
                DiscussionGuidString = properties.ListItem.Properties["DiscItemGuid"].ToString();
                DiscussionGuid = (Guid)TypeDescriptor.GetConverter(typeof(Guid)).ConvertFrom(DiscussionGuidString);
                DiscussionItem = DiscussionItems[DiscussionGuid];
            }
            catch (Exception ex)
            {
                LogMessage("Error handling event for Wiki Discussions " + ex.Message);
            }
            finally
            {
                if (WikiWeb != null) WikiWeb.Dispose();
            }

            if (DiscussionItems == null)
            {
                LogMessage("Unable to find Wiki Discussion list {" + DiscussionListGuidString + "}", TraceProvider.TraceSeverity.Exception);
            }
            else if (DiscussionItem == null)
            {
                LogMessage("Unable to find Wiki Discussion item {" + DiscussionGuidString + "}", TraceProvider.TraceSeverity.WarningEvent);
            }

            return DiscussionItem;
        }


        /// <summary>
        /// Send a message to the logging subsystem.
        /// </summary>
        /// <remarks>Information severity</remarks>
        /// <param name="message">Text message to send</param>
        public void LogMessage(string message)
        {
            LogMessage(message, TraceProvider.TraceSeverity.InformationEvent);
        }

        /// <summary>
        /// Send an exception to the logging subsystem.
        /// </summary>
        /// <remarks>Error severity</remarks>
        /// <param name="message">Text message to preface exception.</param>
        /// <param name="ex">Exception's Message and StackTrace will be logged.</param>
        public void LogMessage(string message, Exception ex)
        {
            LogMessage(message, ex, TraceProvider.TraceSeverity.Exception);
        }

        /// <summary>
        /// Send an exception to the logging subsystem.
        /// </summary>
        /// <param name="message">Text message to preface exception.</param>
        /// <param name="ex">Exception's Message and StackTrace will be logged.</param>
        /// <param name="severity">System.Diagnostics.TraceEventType level of severity</param>
        public void LogMessage(string message, Exception ex, TraceProvider.TraceSeverity severity)
        {
            if (message != "") 
                message += "  " + ex.Message + ".  " + ex.StackTrace;
            else
                message = ex.Message + "  " + ex.StackTrace;
            LogMessage(message, severity);
        }

        /// <summary>
        /// Send a message to the logging subsystem.
        /// </summary>
        /// <param name="message">Text message to send</param>
        /// <param name="severity">System.Diagnostics.TraceEventType level of severity</param>
        public void LogMessage(string message, TraceProvider.TraceSeverity severity)
        {
            Guid CorrelationGuid = new Guid();
            //TODO: Break long messages up into 800 character lines (use same CorrelationGuid for each of these)
            TraceProvider.WriteTrace(0, severity, CorrelationGuid, exeName, productName, CategoryBySeverity(severity), message);
        }


        private string CategoryBySeverity(TraceProvider.TraceSeverity severity)
        {
            string category = "General";

            switch (severity)
            {
                case TraceProvider.TraceSeverity.CriticalEvent:
                    //TraceEventType.Critical
                    category = "Error";
                    break;
                case TraceProvider.TraceSeverity.Exception:
                    //TraceEventType.Error
                    category = "Error";
                    break;
                case TraceProvider.TraceSeverity.InformationEvent:
                    //TraceEventType.Information
                    category = "Debug";
                    break;
                case TraceProvider.TraceSeverity.WarningEvent:
                    //TraceEventType.Warning
                    category = "Debug";
                    break;
            }
            return category;
        }

        //Regular expression to isolate the leafname ("$1") or its path ("$2") from a URL
        //(.*)\/(.*$) splits a string based on the rightmost slash because it's a greedy algorithm.
        public static System.Text.RegularExpressions.Regex rexUrlLeafname =
        new System.Text.RegularExpressions.Regex(
        @"(.*)\/(.*$)",
        System.Text.RegularExpressions.RegexOptions.Compiled);

        //Regular expression to isolate a filename ("$1") or its extension ("$2")
        //(.*)\.(.*$) splits a string based on the rightmost dot because it's a greedy algorithm.
        public static System.Text.RegularExpressions.Regex rexFilename =
        new System.Text.RegularExpressions.Regex(
        @"(.*)\.(.*$)",
        System.Text.RegularExpressions.RegexOptions.Compiled);

    }
}
